//
//  SSLDBModel.m
//  HD_SSL_FMDB
//
//  Created by 恒达的mini on 2017/5/24.
//  Copyright © 2017年 孙世龙. All rights reserved.
//

#import "SSLDBModel.h"
#import "SSLDBManager.h"
#import <objc/runtime.h>

@implementation SSLDBModel
#pragma mark -------------------------------------- init ------------------------------
+(void)initialize{
    if (self != [SSLDBModel self]) {
        [self createTable]; //建表
    }
}
- (instancetype)init{
    self = [super init];
    if (self) {
        NSDictionary *dic = [self.class getModelAllPropertiesIncludePK];
        _columeNames = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"name"]];
        
        _columeTypes = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"type"]];
    }
    return self;
}

#pragma mark -------------------------------------- 建表 -------------------------------
+(BOOL)createTable{
    __block BOOL result = YES;
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    [dbManager.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        //
        NSString *tableName = NSStringFromClass(self.class);
        NSString *columnAndType = [self.class getColumeAndTypeString];//所有表的属性拼接成SQL建表的语句
        NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@);",tableName,columnAndType];
        if (![db executeUpdate:sql]) {//建表失败回滚
            result=NO;
            *rollback = YES;
            return;
        }
        
        NSMutableArray *columns = [NSMutableArray array];//表中所有属性名
        FMResultSet *set = [db getTableSchema:tableName];
        while ([set next]) {
            NSString *column = [set stringForColumn:@"name"];
            [columns addObject:column];
        }
        
        NSDictionary *dict = [self.class getModelAllPropertiesIncludePK];
        NSArray *properties = [dict objectForKey:@"name"];
        NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)",columns];
        
        //过滤数组
        NSArray *resultArray = [properties filteredArrayUsingPredicate:filterPredicate];
        for (NSString *column in resultArray) {
            NSUInteger index = [properties indexOfObject:column];
            NSString *proType = [[dict objectForKey:@"type"] objectAtIndex:index];
            NSString *fieldSql = [NSString stringWithFormat:@"%@ %@",column,proType];
            NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN %@ ",NSStringFromClass(self.class),fieldSql];
            if (![db executeUpdate:sql]) {
                result = NO;
                *rollback = YES;
                return ;
            }
        }
        
    }];
    
    return result;
}

#pragma mark -------------------------------------- 增删改查 ----------------------------------

#pragma mark --------------------------------- 增 --------
#pragma mark - 保存单个对象
- (BOOL)saveObject{
    NSString *tableName = NSStringFromClass(self.class);
    NSMutableString *keyString = [NSMutableString string];//所有属性字符串
    NSMutableString *valueString = [NSMutableString string];//所有值字符串
    NSMutableArray *insertValues = [NSMutableArray  array];//对象的所有属性值
    
    for (int i = 0; i < self.columeNames.count; i++) {
        NSString *proname = [self.columeNames objectAtIndex:i];
        if ([proname isEqualToString:primaryId]) {
            continue;
        }
        [keyString appendFormat:@"%@,", proname];
        [valueString appendString:@"?,"];//默认先是空，插入后再赋值
        id value = [self valueForKey:proname];
        if (!value) {
            value = @"";
        }
        [insertValues addObject:value];
    }
    
    //删除最后的点
    [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
    [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)];
    
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    __block BOOL res = NO;
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString];
        res = [db executeUpdate:sql withArgumentsInArray:insertValues];
        self.pk = res?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
//        NSLog(res?@"插入成功":@"插入失败");
    }];
    return res;

}
#pragma mark - 批量保存用户对象
+ (BOOL)saveObjects:(NSArray *)array{
    //判断是否是SSLDBModel的子类
    for (SSLDBModel *model in array) {
        if (![model isKindOfClass:[SSLDBModel class]]) {
            return NO;
        }
    }
    
    __block BOOL res = YES;
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    // 如果要支持事务
    [dbManager.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (SSLDBModel *model in array) {
            NSString *tableName = NSStringFromClass(model.class);
            NSMutableString *keyString = [NSMutableString string];
            NSMutableString *valueString = [NSMutableString string];
            NSMutableArray *insertValues = [NSMutableArray  array];
            for (int i = 0; i < model.columeNames.count; i++) {
                NSString *proname = [model.columeNames objectAtIndex:i];
                if ([proname isEqualToString:primaryId]) {
                    continue;
                }
                [keyString appendFormat:@"%@,", proname];
                [valueString appendString:@"?,"];
                id value = [model valueForKey:proname];
                if (!value) {
                    value = @"";
                }
                [insertValues addObject:value];
            }
            [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
            [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)];
            
            NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:insertValues];
            model.pk = flag?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
//            NSLog(flag?@"插入成功":@"插入失败");
            
            //插入失败，回滚
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];
    return res;
}
#pragma mark - 保存或更新对象
- (BOOL)saveOrUpdate{
    id primaryValue = [self valueForKey:primaryId];
    if ([primaryValue intValue]<=0) {
        return [self saveObject];
    }
    return [self updateObject];
}
#pragma mark - 根据一对属性值更新或保存
- (BOOL)saveOrUpdateByColumnName:(NSString *)columnName AndColumnValue:(NSString *)columnValue{
    id record = [self.class findFirstByCriteria:[NSString stringWithFormat:@"where %@ = %@",columnName,columnValue]];
    if (record) {
        id primaryValue = [record valueForKey:primaryId]; //取到了主键PK
        if ([primaryValue intValue] <= 0) {
            return [self saveObject];
        }else{
            self.pk = [primaryValue intValue];
            return [self updateObject];
        }
    }else{
        return [self saveObject];
    }
}

#pragma mark---------------------------------- 删 ----------
#pragma mark - 删除单个对象
- (BOOL)deleteObject{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    __block BOOL res = NO;
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        id primaryValue = [self valueForKey:primaryId];
        if (!primaryValue || primaryValue <= 0) {
            return ;
        }
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId];
        res = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]];
        NSLog(res?@"删除成功":@"删除失败");
    }];
    return res;
}
#pragma mark - 删除一组对象
+ (BOOL)deleteObjects:(NSArray *)array{
    for (SSLDBModel *model in array) {
        if (![model isKindOfClass:[SSLDBModel class]]) {
            return NO;
        }
    }
    
    __block BOOL res = YES;
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    // 如果要支持事务
    [dbManager.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (SSLDBModel *model in array) {
            NSString *tableName = NSStringFromClass(model.class);
            id primaryValue = [model valueForKey:primaryId];
            if (!primaryValue || primaryValue <= 0) {
                return ;
            }
            
            NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]];
            //NSLog(flag?@"删除成功":@"删除失败");
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];
    return res;
}
#pragma mark - 条件删除
+ (BOOL)deleteObjectsByCriteria:(NSString *)criteria{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    __block BOOL res = NO;
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ %@ ",tableName,criteria];
        res = [db executeUpdate:sql];
        //NSLog(res?@"删除成功":@"删除失败");
    }];
    return res;
}
#pragma mark - 规则删除
+ (BOOL)deleteObjectsWithFormat:(NSString *)format, ...{
    va_list ap;
    va_start(ap, format);
    NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap];
    va_end(ap);
    
    return [self deleteObjectsByCriteria:criteria];
}
#pragma mark - 清空表
+ (BOOL)clearTable{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    __block BOOL res = NO;
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@",tableName];
        res = [db executeUpdate:sql];
        //NSLog(res?@"清空成功":@"清空失败");
    }];
    return res;
}

#pragma mark --------------------------------- 改 ---------
#pragma mark - 更新一个对象
- (BOOL)updateObject{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    __block BOOL res = NO;
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        //
        NSString *tableName = NSStringFromClass(self.class);
        id primaryValue = [self valueForKey:primaryId];
        if (!primaryValue||primaryValue<=0) {
            return ;
        }
        
        NSMutableString *keyString = [NSMutableString string];
        NSMutableArray *updateValues = [NSMutableArray  array];
        for (int i = 0; i < self.columeNames.count; i++) {
            NSString *proname = [self.columeNames objectAtIndex:i];
            if ([proname isEqualToString:primaryId]) {
                continue;
            }
            [keyString appendFormat:@" %@=?,", proname];
            id value = [self valueForKey:proname];
            if (!value) {
                value = @"";
            }
            [updateValues addObject:value];
        }
        
        //删除最后那个逗号
        [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?;", tableName, keyString, primaryId];
        [updateValues addObject:primaryValue];
        res = [db executeUpdate:sql withArgumentsInArray:updateValues];
        //NSLog(res?@"更新成功":@"更新失败");
    }];
    
    return res;
}
#pragma mark - 更新一组对象
+ (BOOL)updateObjects:(NSArray *)array{
    for (SSLDBModel *model in array) {
        if (![model isKindOfClass:[SSLDBModel class]]) {
            return NO;
        }
    }
    __block BOOL res = YES;
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    // 如果要支持事务
    [dbManager.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (SSLDBModel *model in array) {
            NSString *tableName = NSStringFromClass(model.class);
            id primaryValue = [model valueForKey:primaryId];
            if (!primaryValue || primaryValue <= 0) {
                res = NO;
                *rollback = YES;
                return;
            }
            
            NSMutableString *keyString = [NSMutableString string];
            NSMutableArray *updateValues = [NSMutableArray  array];
            for (int i = 0; i < model.columeNames.count; i++) {
                NSString *proname = [model.columeNames objectAtIndex:i];
                if ([proname isEqualToString:primaryId]) {
                    continue;
                }
                [keyString appendFormat:@" %@=?,", proname];
                id value = [model valueForKey:proname];
                if (!value) {
                    value = @"";
                }
                [updateValues addObject:value];
            }
            
            //删除最后那个逗号
            [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
            NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@=?;", tableName, keyString, primaryId];
            [updateValues addObject:primaryValue];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:updateValues];
            //NSLog(flag?@"更新成功":@"更新失败");
            
            //更新失败，回滚
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];
    
    return res;
}

#pragma mark---------------------------------- 查 ----------
#pragma mark - 规则查询第一个
+ (instancetype)findFirstWithFormat:(NSString *)format, ...{
    va_list ap;
    va_start(ap, format);
    NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap];
    va_end(ap);
    
    return [self findFirstByCriteria:criteria];
}
+ (instancetype)findFirstByCriteria:(NSString *)criteria{
    NSArray *results = [self.class findByCriteria:criteria];
    if (results.count < 1) {
        return nil;
    }
    
    return [results firstObject];
}
#pragma mark - 主键查询
+ (instancetype)findByPK:(int)inPk{
    NSString *condition = [NSString stringWithFormat:@"WHERE %@=%d",primaryId,inPk];
    return [self findFirstByCriteria:condition];
}
#pragma mark - 格式查询
+ (NSArray *)findWithFormat:(NSString *)format, ...{
    va_list ap;
    va_start(ap, format);
    NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap];
    va_end(ap);
    
    return [self findByCriteria:criteria];
}
#pragma mark - 通过条件查找数据
+ (NSArray *)findByCriteria:(NSString *)criteria{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    NSMutableArray *modelArray = [NSMutableArray array];
    
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ %@",tableName,criteria];
        
        FMResultSet *resultSet = [db executeQuery:sql];
        
        while ([resultSet next]) {
            SSLDBModel *model = [[self.class alloc] init];//初始化对象
            
            for (int i=0; i< model.columeNames.count; i++) {
                NSString *columeName = [model.columeNames objectAtIndex:i];
                NSString *columeType = [model.columeTypes objectAtIndex:i];
                if ([columeType isEqualToString:SQLTEXT]) {
                    [model setValue:[resultSet stringForColumn:columeName] forKey:columeName];
                } else if ([columeType isEqualToString:SQLBLOB]) {
                    [model setValue:[resultSet dataForColumn:columeName] forKey:columeName];
                } else {
                    [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName];
                }
            }
            
            [modelArray addObject:model];
            
            FMDBRelease(model);
        }
    }];
    
    return modelArray;
}
#pragma mark - 查询全部数据
+ (NSArray *)findAllModel{
    //NSLog(@"SSLDB---%s",__func__);
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    NSMutableArray *modelArray = [NSMutableArray array];
    
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@",tableName];
        
        FMResultSet *resultSet = [db executeQuery:sql];
        while ([resultSet next]) {
            SSLDBModel *model = [[self.class alloc] init];
            for (int i=0; i< model.columeNames.count; i++) {
                NSString *columeName = [model.columeNames objectAtIndex:i];
                NSString *columeType = [model.columeTypes objectAtIndex:i];
                if ([columeType isEqualToString:SQLTEXT]) {
                    [model setValue:[resultSet stringForColumn:columeName] forKey:columeName];
                } else if ([columeType isEqualToString:SQLBLOB]) {
                    [model setValue:[resultSet dataForColumn:columeName] forKey:columeName];
                } else {
                    [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName];
                }
            }
            
            [modelArray addObject:model];
            FMDBRelease(model);
        }
    }];
    
    return modelArray;
}

#pragma mark -------------------------------------- 公共方法 ----------------------------
#pragma mark - must be override method，保留字段
+ (NSArray *)transients{
    return [NSArray array];
}
#pragma mark - 判断数据库中是否存在这个表
+(BOOL)isExistInTable{
    __block BOOL result = NO;
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        result = [db tableExists:tableName];
    }];
    return result;
}
#pragma mark - 组装SQL语句建表字段部分
+ (NSString *)getColumeAndTypeString{
    NSMutableString* pars = [NSMutableString string];
    NSDictionary *dict = [self.class getModelAllPropertiesIncludePK];
    
    NSMutableArray *proNames = [dict objectForKey:@"name"];
    NSMutableArray *proTypes = [dict objectForKey:@"type"];
    
    for (int i=0; i< proNames.count; i++) {
        [pars appendFormat:@"%@ %@",[proNames objectAtIndex:i],[proTypes objectAtIndex:i]];
        if(i+1 != proNames.count)
        {
            [pars appendString:@","];
        }
    }
    return pars;
}
#pragma mark - 获取类所有属性
+(NSDictionary*)getModelAllPropertys{
    NSMutableArray *proNames = [NSMutableArray array];
    NSMutableArray *proTypes = [NSMutableArray array];
    NSArray        *theTransients = [[self class] transients];//不需要创建数据库字段
    
    unsigned int outCount,i;
    
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    
    for (i=0; i<outCount; i++) {
        objc_property_t property = properties[i];
        
        //1.获取属性的名字
        NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        if ([theTransients containsObject:propertyName]) {
            continue;//跳过
        }
        //加入数组
        [proNames addObject:propertyName];
        
        //2.获取属性的类型
        NSString *propertyType = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
        //加入数组
        if ([propertyType hasPrefix:@"T@\"NSString\""]) {
            [proTypes addObject:SQLTEXT];
        } else if ([propertyType hasPrefix:@"T@\"NSData\""]) {
            [proTypes addObject:SQLBLOB];
        } else if ([propertyType hasPrefix:@"Ti"]||[propertyType hasPrefix:@"TI"]||[propertyType hasPrefix:@"Ts"]||[propertyType hasPrefix:@"TS"]||[propertyType hasPrefix:@"TB"]||[propertyType hasPrefix:@"Tq"]||[propertyType hasPrefix:@"TQ"]) {
            [proTypes addObject:SQLINTEGER];
        } else {
            [proTypes addObject:SQLREAL];
        }
        /*
         各种符号对应类型，部分类型在新版SDK中有所变化，如long 和long long
         c char         C unsigned char
         i int          I unsigned int
         l long         L unsigned long
         s short        S unsigned short
         d double       D unsigned double
         f float        F unsigned float
         q long long    Q unsigned long long
         B BOOL
         @ 对象类型 //指针 对象类型 如NSString 是@“NSString”
         
         
         64位下long 和long long 都是Tq
         SQLite 默认支持五种数据类型TEXT、INTEGER、REAL、BLOB、NULL
         这里只写了部分类型类型，常用开发足够
         */
        
    }
    free(properties);
    
    return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type", nil];
    
}
#pragma mark - 获取所有属性，包括主键值
+(NSDictionary*)getModelAllPropertiesIncludePK{
    NSDictionary *dict = [self.class getModelAllPropertys];
    NSMutableArray *proNames = [NSMutableArray array];
    NSMutableArray *proTypes = [NSMutableArray array];
    
    //添加主键
    [proNames addObject:primaryId];
    [proTypes addObject:[NSString stringWithFormat:@"%@ %@",SQLINTEGER,PrimaryKey]];
    
    [proNames addObjectsFromArray:[dict objectForKey:@"name"]];
    [proTypes addObjectsFromArray:[dict objectForKey:@"type"]];
    
    return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type", nil];
}
#pragma mark - 获取列名
+(NSArray *)getModelColumns{
    SSLDBManager *dbManager = [SSLDBManager shareInstance];
    NSMutableArray *columnArray = [NSMutableArray array];
    [dbManager.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        FMResultSet *set = [db getTableSchema:tableName];
        if ([set next]) {
            NSString *column = [set stringForColumn:@"name"];
            [columnArray addObject:column];
        }
    }];
    return columnArray;
}
#pragma mark - description
- (NSString *)description{
    NSString *result = @"";
    NSDictionary *dict = [self.class getModelAllPropertiesIncludePK];
    NSMutableArray *proNames = [dict objectForKey:@"name"];
    for (int i = 0; i < proNames.count; i++) {
        NSString *proName = [proNames objectAtIndex:i];
        id  proValue = [self valueForKey:proName];
        result = [result stringByAppendingFormat:@"%@:%@\n",proName,proValue];
    }
    return result;
}


@end
